package com.example.fileupload.storage.impl;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.example.fileupload.storage.AbstractStorageStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 阿里云OSS存储策略实现 - 函数式编程风格
 */
@Slf4j
@Component
@ConditionalOnProperty(name = "storage.oss.enabled", havingValue = "true")
public class OssStorageStrategy extends AbstractStorageStrategy {
    
    @Value("${storage.oss.endpoint}")
    private String endpoint;
    
    @Value("${storage.oss.access-key-id}")
    private String accessKeyId;
    
    @Value("${storage.oss.access-key-secret}")
    private String accessKeySecret;
    
    @Value("${storage.oss.bucket-name}")
    private String bucketName;
    
    @Value("${storage.oss.url-expiry:7}")
    private int urlExpiryDays;
    
    private OSS ossClient;
    
    @PostConstruct
    public void init() {
        this.ossClient = new OSSClientBuilder()
                .build(endpoint, accessKeyId, accessKeySecret);
        
        // 确保bucket存在
        ensureBucketExists()
                .ifPresentOrElse(
                        created -> log.info("OSS存储策略初始化完成, bucket: {}", bucketName),
                        () -> log.error("OSS存储策略初始化失败")
                );
    }
    
    @PreDestroy
    public void destroy() {
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }
    
    @Override
    public StorageType getStorageType() {
        return StorageType.OSS;
    }
    
    @Override
    protected StorageResult doStoreChunk(String chunkPath, InputStream inputStream, long size) {
        try {
            // 上传对象到OSS
            PutObjectRequest request = new PutObjectRequest(bucketName, chunkPath, inputStream);
            PutObjectResult result = ossClient.putObject(request);
            
            log.debug("OSS分片上传成功: {}", chunkPath);
            
            return StorageResult.success(
                    chunkPath,
                    generateObjectUrl(chunkPath),
                    size
            );
            
        } catch (Exception e) {
            log.error("OSS存储分片失败: {}", e.getMessage());
            return StorageResult.failure("OSS存储分片失败: " + e.getMessage());
        }
    }
    
    @Override
    protected StorageResult doMergeChunks(String finalPath, List<String> chunkPaths, long totalSize) {
        try {
            // 从OSS下载所有分片并合并
            ByteArrayOutputStream mergedStream = new ByteArrayOutputStream();
            
            chunkPaths.stream()
                    .forEach(chunkPath -> {
                        try {
                            OSSObject ossObject = ossClient.getObject(bucketName, chunkPath);
                            try (InputStream chunkStream = ossObject.getObjectContent()) {
                                chunkStream.transferTo(mergedStream);
                                log.debug("下载并合并OSS分片: {}", chunkPath);
                            }
                        } catch (Exception e) {
                            throw new RuntimeException("合并分片失败: " + chunkPath, e);
                        }
                    });
            
            // 上传合并后的文件
            byte[] mergedData = mergedStream.toByteArray();
            PutObjectRequest putRequest = new PutObjectRequest(
                    bucketName, 
                    finalPath, 
                    new ByteArrayInputStream(mergedData)
            );
            
            ossClient.putObject(putRequest);
            log.info("OSS文件合并完成: {}, 总大小: {}", finalPath, totalSize);
            
            return StorageResult.success(
                    finalPath,
                    generateObjectUrl(finalPath),
                    totalSize
            );
            
        } catch (Exception e) {
            log.error("OSS合并文件失败: {}", e.getMessage());
            return StorageResult.failure("OSS合并文件失败: " + e.getMessage());
        }
    }
    
    @Override
    protected boolean doDeleteFile(String path) {
        try {
            ossClient.deleteObject(bucketName, path);
            log.debug("删除OSS对象: {}", path);
            return true;
            
        } catch (Exception e) {
            log.error("删除OSS对象失败: {}, 错误: {}", path, e.getMessage());
            return false;
        }
    }
    
    @Override
    public boolean exists(String path) {
        try {
            return ossClient.doesObjectExist(bucketName, path);
        } catch (Exception e) {
            return false;
        }
    }
    
    @Override
    public String getDownloadUrl(String path) {
        try {
            // 生成预签名URL
            Date expiration = Date.from(
                    LocalDateTime.now()
                            .plusDays(urlExpiryDays)
                            .atZone(ZoneId.systemDefault())
                            .toInstant()
            );
            
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
                    bucketName, path, HttpMethod.GET
            );
            request.setExpiration(expiration);
            
            return ossClient.generatePresignedUrl(request).toString();
            
        } catch (Exception e) {
            log.error("获取OSS下载URL失败: {}", e.getMessage());
            throw new RuntimeException("获取OSS下载URL失败: " + e.getMessage());
        }
    }
    
    // ============ 私有辅助方法 ============
    
    /**
     * 确保bucket存在
     */
    private Optional<Boolean> ensureBucketExists() {
        try {
            if (!ossClient.doesBucketExist(bucketName)) {
                CreateBucketRequest request = new CreateBucketRequest(bucketName);
                request.setCannedACL(CannedAccessControlList.Private);
                ossClient.createBucket(request);
                log.info("创建OSS bucket: {}", bucketName);
            }
            
            return Optional.of(true);
            
        } catch (Exception e) {
            log.error("确保OSS bucket存在失败: {}", e.getMessage());
            return Optional.empty();
        }
    }
    
    /**
     * 生成对象URL
     */
    private String generateObjectUrl(String objectName) {
        return String.format("https://%s.%s/%s", bucketName, endpoint, objectName);
    }
    
    /**
     * 列出分片对象
     */
    private List<String> listChunkObjects(String md5) {
        try {
            String prefix = "chunks/" + md5 + "/";
            ListObjectsRequest request = new ListObjectsRequest(bucketName);
            request.setPrefix(prefix);
            
            ObjectListing listing = ossClient.listObjects(request);
            return listing.getObjectSummaries()
                    .stream()
                    .map(OSSObjectSummary::getKey)
                    .collect(Collectors.toList());
                    
        } catch (Exception e) {
            log.error("列出OSS分片对象失败: {}", e.getMessage());
            return List.of();
        }
    }
    
    /**
     * 批量删除分片
     */
    public boolean deleteChunks(List<String> objectNames) {
        try {
            if (objectNames.isEmpty()) {
                return true;
            }
            
            DeleteObjectsRequest request = new DeleteObjectsRequest(bucketName);
            request.setKeys(objectNames);
            
            DeleteObjectsResult result = ossClient.deleteObjects(request);
            
            // 检查删除结果
            if (!result.getDeletedObjects().isEmpty()) {
                log.debug("成功删除OSS对象: {}", result.getDeletedObjects().size());
            }
            return true;
            
        } catch (Exception e) {
            log.error("批量删除OSS对象失败: {}", e.getMessage());
            return false;
        }
    }
    
    // ============ 钩子方法重写 ============
    
    @Override
    protected Optional<StorageResult> postMergeChunks(StorageResult result) {
        // 合并完成后，异步删除分片
        cleanupChunksAsync(result.getPath());
        return Optional.of(result);
    }
    
    /**
     * 异步清理分片文件
     */
    private void cleanupChunksAsync(String finalPath) {
        CompletableFuture.runAsync(() -> {
            extractMd5FromPath(finalPath)
                    .ifPresent(md5 -> {
                        List<String> chunkObjects = listChunkObjects(md5);
                        boolean deleted = deleteChunks(chunkObjects);
                        if (deleted) {
                            log.debug("异步清理OSS分片完成: {}", chunkObjects.size());
                        } else {
                            log.warn("异步清理OSS分片失败");
                        }
                    });
        }).exceptionally(throwable -> {
            log.warn("异步清理OSS分片失败: {}", throwable.getMessage());
            return null;
        });
    }
    
    /**
     * 从文件路径提取MD5
     */
    private Optional<String> extractMd5FromPath(String path) {
        try {
            String[] parts = path.split("/");
            return parts.length >= 2 ? 
                    Optional.of(parts[parts.length - 2]) : 
                    Optional.empty();
        } catch (Exception e) {
            return Optional.empty();
        }
    }
} 